package org.sibframework.utils;
import java.beans.PropertyDescriptor;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.apache.commons.beanutils.PropertyUtils;



public class Describer {
	private static final String _ = "<"; 
	private static final String _2 = ">--------------------------------------------"; 
	private static final String _u = "</"; 
	private static final String _u2 = ">--------------------------------------------"; 

	public static void describe(Object obj) {
		describe(obj, new PrintWriter(System.out), "");
	}
	
	public static void describe(Object obj, PrintWriter pw, String pre) {
		
		if (pw == null) {
			return;
		}

		if (obj == null) {
			pw.println("(null)");
			return;
		}
		
		if (pre == null) {
			pre = "";
		}
		
		//for primitive type : simple print a line.
		if (obj instanceof String || obj.getClass().getName().startsWith("java.lang.")) {
			pw.println(obj);
		} else if (obj instanceof Collection) {
			describe((Collection<?>) obj, pw, pre) ;
		} else if (obj instanceof Iterable) {
			describe((Iterable<?>) obj, pw, pre) ;
		} else if (obj instanceof Map) {
			describe((Map) obj, pw, pre) ;
		} else if (obj instanceof String[]) {
			describe((String[]) obj, pw, pre) ;
		} else if (obj instanceof Object[]) {
			describe((Object[]) obj, pw, pre) ;
		} else if (obj.getClass().getName().startsWith("java") || obj.getClass().getName().startsWith("sun")) {
			pw.println(obj);
		}else {
			pw.println("\n" + pre + _ + obj.getClass().getSimpleName() + _2);
			describeObject(obj, pw, pre);
			pw.println(pre + _u + obj.getClass().getSimpleName() + _u2 + "\n");
		}
		
		
		pw.flush();
	}
	
	private static void describe(Collection<?> coles, PrintWriter pw, String pre) {		
		describe(coles.toArray(), pw, pre);
	}
	
	private static void describe(Iterable<?> ita, PrintWriter pw, String pre) {
		Iterator<?> it = ita.iterator();
		while (it.hasNext()) {
			try {
				Object o = it.next();
				describe(o, pw, pre);
			} catch (Exception e) {
				pw.println("(Error:" + e.getLocalizedMessage() + ")");
				e.printStackTrace();
			}
		}
	}
	
	private static void describe(Map map, PrintWriter pw, String pre) {
		Set keys = map.keySet();
		try {
			keys = new TreeSet(keys);
		} catch (Exception e) {
		}
		
		for (Object o : keys)  {
			try {
				if (o instanceof String || ReflectTool.isPrimitive(o.getClass())) {
					pw.print(o);
				} else {
					describe(o, pw, pre);
				}
				o = map.get(o);
				if (o instanceof String || ReflectTool.isPrimitive(o.getClass())) {
					pw.print(" = ");
					pw.print(o);
					pw.print(", \n");
				} else {
					pw.println(pre + " = {");
					describe(o, pw, pre + "\t");
					pw.println(pre + "}\n");
				}
			} catch (Exception e) {
				pw.println("(Error:" + e.getLocalizedMessage() + ")");
				e.printStackTrace();
			}
		}
	}

	private static void describe(Object[] objs, PrintWriter pw, String pre) {
		pw.println(pre + "[ ");
		for (Object o : objs) {
			try {
				if (o instanceof String || ReflectTool.isPrimitive(o.getClass())) {
					pw.print(pre + o);
					pw.println(", ");
				} else {
					describe(o, pw, pre + "\t");
				}
			} catch (Exception e) {
				pw.println("(Error:" + e.getLocalizedMessage() + ")");
				e.printStackTrace();
			}
		}
		pw.println(pre + "]");
		pw.println();
	}

	private static void describe(String[] objs, PrintWriter pw, String pre) {
		pw.println(pre + "[ ");
		for (String s : objs) {
			try {
				pw.print(pre + s);
				pw.println(", ");
			} catch (Exception e) {
				pw.println("(Error:" + e.getLocalizedMessage() + ")");
				e.printStackTrace();
			}
/*			int i = (int) Math.ceil((16 - s.length())/8d);
			for (int index = 0; index < i; index++) {
				pw.print("\t");
			}*/
		}
		pw.println(pre + "]");
		pw.println();
	}

	
	//反射描述一个对象
	private static void describeObject2(Object obj, PrintWriter pw, String pre) {
		PropertyDescriptor[] pds = PropertyUtils.getPropertyDescriptors(obj.getClass());
		TreeSet<String> ts = new TreeSet<String>();
		
		for (PropertyDescriptor pd : pds) {
			if (pd.getName().equalsIgnoreCase("class")) {
				continue;
			}
			ts.add(pd.getName());
		}
		
		for (String pd : ts) {
			try {
				String name = pd;
				pw.print(pre + name);
				if (name.length() < 8) {
					pw.print("\t");
				}
				pw.print(": ");
				Object o = PropertyUtils.getProperty(obj, pd);
				describe(o, pw, pre + "\t");

			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
	}
	
	
	private static void describeObject(Object obj, PrintWriter pw, String pre) {
		Map<String, String> gns = new HashMap<String, String>();

		//TreeSet<Field> ts = new TreeSet<Field>(Arrays.asList(obj.getClass().getDeclaredFields()));
		for (Field field : obj.getClass().getDeclaredFields()) {
			if (field.isAccessible()) {
				try {
					describe(field.get(obj), pw, pre + "\t");
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			} else {
				Naming getterNaming = Naming.createByFieldName(field.getName());
				gns.put(getterNaming.getMethodname("get"), getterNaming.getFieldName());
				gns.put(getterNaming.getMethodname("is"), getterNaming.getFieldName());
			}
		}

		//TreeSet<Method> ts2 = new TreeSet<Method>(Arrays.asList(obj.getClass().getDeclaredMethods()));
		for (Method method : obj.getClass().getDeclaredMethods()) {
			if (gns.get(method.getName()) != null && method.getParameterTypes().length == 0 && method.getReturnType() != null) {
				try {
					String name = gns.get(method.getName());
					pw.print(pre + name);
					if (name.length() < 8) {
						pw.print("\t");
					}
					pw.print(": ");
					describe(method.invoke(obj, new Object[0]), pw, pre + "\t");
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
}
